from __future__ import print_function

import socket
import struct
import time
import ssl
from bcc import BPF
import json
import numpy as np

# define BPF program
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <net/sock.h>
#include <bcc/proto.h>
BPF_HASH(currsock, u32, struct sock *);
int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk)
{
    u32 pid = bpf_get_current_pid_tgid();
    // stash the sock ptr for lookup on return
    currsock.update(&pid, &sk);
    return 0;
};
int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
{
    int ret = PT_REGS_RC(ctx);
    u32 pid = bpf_get_current_pid_tgid();
    struct sock **skpp;
    skpp = currsock.lookup(&pid);
    if (skpp == 0) {
        return 0;	// missed entry
    }
    if (ret != 0) {
        // failed to send SYNC packet, may not have populated
        // socket __sk_common.{skc_rcv_saddr, ...}
        currsock.delete(&pid);
        return 0;
    }
    // pull in details
    struct sock *skp = *skpp;
    u32 saddr = 0, daddr = 0;
    u16 dport = 0;
    bpf_probe_read(&saddr, sizeof(saddr), &skp->__sk_common.skc_rcv_saddr);
    bpf_probe_read(&daddr, sizeof(daddr), &skp->__sk_common.skc_daddr);
    bpf_probe_read(&dport, sizeof(dport), &skp->__sk_common.skc_dport);
    // output
    bpf_trace_printk("trace_tcp4connect %x %x %d\\n", saddr, daddr, ntohs(dport));
    currsock.delete(&pid);
    return 0;
}
"""

# initialize BPF
b = BPF(text=bpf_text)


def inet_ntoa(addr):
    dq = ''
    for i in range(0, 4):
        dq = dq + str(addr & 0xff)
        if i != 3:
            dq = dq + '.'
        addr = addr >> 8
    return dq


class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        elif isinstance(obj, bytes):
            return str(obj, encoding='utf-8');
        return json.JSONEncoder.default(self, obj)


so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "127.0.0.1"
port = 8887
CA_FILE = "/home/a/ou/key/ca.crt"
KEY_FILE = "/home/a/ou/key/client.key"
CERT_FILE = "/home/a/ou/key/client.crt"
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.check_hostname = False
context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
context.load_verify_locations(CA_FILE)
context.verify_mode = ssl.CERT_REQUIRED
sock = context.wrap_socket(so, server_side=False)
sock.connect((host, port))

# filter and format output
while 1:
    # Read messages from kernel pipe
    try:
        (task, pid, cpu, flags, ts, msg) = b.trace_fields()
        (_tag, saddr_hs, daddr_hs, dport_s) = msg.decode().split(" ")
    except ValueError:
        # Ignore messages from other tracers
        continue
    # Ignore messages from other tracers
    if _tag != "trace_tcp4connect":
        continue
    saddr = inet_ntoa(int(saddr_hs, 16))
    daddr = inet_ntoa(int(daddr_hs, 16))
    dport = dport_s
    tcp = {'type': 'tcp', 'pid': pid, 'comm': task, 'saddr': saddr, 'daddr': daddr, 'dport': dport}
    json_tcp = json.dumps(tcp, cls=MyEncoder, indent=4).encode()
    data = struct.pack("I%ds" % (len(json_tcp),), len(json_tcp), json_tcp)
    # print(json_tcp)
    sock.send(data)

sock.close()

